package org.opfx.tools.ant.php.taskdefs;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.FilterSet;
import org.apache.tools.ant.types.FilterSetCollection;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.ResourceUtils;
import org.opfx.tools.ant.php.util.PHP;

public class Lint extends Task {

	protected LintCopy copier;

	protected boolean failOnError;

	protected File sourceDir;
//	protected File targetDir;

	public Lint() {
		
		this.sourceDir = null;
		this.failOnError = true;
	}

	public void init() throws BuildException {
		copier = new LintCopy();

		// configure the copy task
		copier.setProject(getProject());
		copier.setTaskName(getTaskName());
		copier.setOwningTarget(getOwningTarget());
		copier.init();

		copier.setIncludeEmptyDirs(false);
		copier.setPreserveLastModified(true);

	}

	public void execute() throws BuildException {
		try {
			validateParams();

			File targetDir = copier.getDestDir();

			boolean skipRemove = false;

			if (!targetDir.exists() || targetDir.list().length < 1) {
				skipRemove = true;
			}

			copier.execute();

			// remove obsolete files only if there are any
			if (skipRemove) {
				log("Skipping removal of obsolete files from " + targetDir,
						Project.MSG_DEBUG);
				return;
			}

			removeJunkFiles(targetDir);
			removeEmptyDirs(targetDir);

		} catch (BuildException e) {
			if (!this.failOnError) {
				String message = e.getMessage();
				if (message == null) {
					message = e.toString();
				}
				log("Warning: " + message, Project.MSG_ERR);
			} else {
				throw e;
			}
		} finally {
			
		}
	}

	// //////////////////////////////////////////////////////
	// PARAMETERS

	public void addFileset(FileSet set) {
		copier.addFileset(set);
	}

	/**
	 * Sets whether to fail when an error is encountered.
	 * 
	 * @param failOnError
	 *            
	 */
	public void setFailOnError(boolean failOnError) {
		this.failOnError = failOnError;
	}

	public void setSrcdir(File srcDir) {
		this.sourceDir = srcDir;
	}
	
	public void setDestdir(File destDir) {
		copier.setTodir(destDir);
	}

	// //////////////////////////////////////////////////////
	// OPERATIONS

	private void removeEmptyDirs(File dir, boolean removeIfEmpty) {

		// File targetDir = copier.getTargetDir();
		log("Removing empty directories from " + dir, Project.MSG_DEBUG);

		if (dir.isDirectory()) {
			File[] files = dir.listFiles();
			File file;
			for (int i = 0; i < files.length; i++) {
				file = files[i];
				if (file.isDirectory()) {
					removeEmptyDirs(file, true);
				}
			}

			if (files.length > 0) {
				files = dir.listFiles();
			}

			if (files.length < 1 && removeIfEmpty) {
				log("Removing empty directory " + dir, Project.MSG_DEBUG);
				dir.delete();
			}
		}
	}

	private void removeEmptyDirs(File dir) {
		removeEmptyDirs(dir, false);
	}

	private void removeJunkFiles(File dir) {

		log("Removing obsolete files from " + dir, Project.MSG_DEBUG);

		String[] dump;
		String[] keep = (String[]) copier.keep.toArray(new String[copier.keep
				.size() + 1]);
		keep[copier.keep.size()] = "";

		DirectoryScanner ds = null;

		ds = new DirectoryScanner();
		ds.setBasedir(dir);

		ds.addExcludes(keep);

		ds.scan();
		dump = ds.getIncludedFiles();

		File junkfile;
		for (int i = 0; i < dump.length; i++) {
			junkfile = new File(dir, dump[i]);
			log("Removing obsolete file " + junkfile, Project.MSG_DEBUG);
			junkfile.delete();
		}
		junkfile = null;

	}

	protected void validateParams() throws BuildException {
		if(this.sourceDir!=null) {
			String [] includes= {"**/*.php"};
			FileSet fileset = new FileSet();
			fileset.setDir(this.sourceDir);
			fileset.appendIncludes(includes);
			
			this.addFileset(fileset);
			
			fileset = null;
		}
		// throw new BuildException("Some message");
	}

	private static class LintCopy extends Copy {

		private Set keep;

		@SuppressWarnings("rawtypes")
		public LintCopy() {
			super();
			keep = new HashSet();
			fileUtils = new LintFileUtils();
		}

		public File getDestDir() {
			return destDir;
		}

		protected void scan(File fromDir, File toDir, String[] files,
				String[] dirs) {
			super.scan(fromDir, toDir, files, dirs);

			for (int i = 0; i < files.length; ++i) {
				keep.add(files[i]);
			}
			for (int i = 0; i < dirs.length; ++i) {
				keep.add(dirs[i]);
			}
		}

		@SuppressWarnings("rawtypes")
		protected Map scan(Resource[] resources, File toDir) {
			Map m = super.scan(resources, toDir);

			Iterator iter = m.keySet().iterator();
			while (iter.hasNext()) {
				keep.add(((Resource) iter.next()).getName());
			}
			return m;
		}

		public class LintFileUtils extends FileUtils {

			public void copyFile(File sourceFile, File destFile,
					FilterSetCollection filters, Vector filterChains,
					boolean overwrite, boolean preserveLastModified,
					boolean append, String inputEncoding,
					String outputEncoding, Project project, boolean force)
					throws IOException {

				// here we add the actual lint
				PHP.getInstance(getProject()).lint(sourceFile);

				ResourceUtils.copyResource(new FileResource(sourceFile),
						new FileResource(destFile), filters, filterChains,
						overwrite, preserveLastModified, append, inputEncoding,
						outputEncoding, project, force);
			}
		}
	}

}
